package cn.icanci.snowstack.codediff;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

/**
 * @author icanci
 * @date Created in 2021/06/28 14:01
 * @version 1.0
 */
public class ObjectComparisonUtils {
    /** 
     * 日志 
     */
    private static Logger      logger = LoggerFactory.getLogger(ObjectComparisonUtils.class);
    /** 
     * 箭头 
     */
    public static final String ARROWS = "=>";

    /**
     * 比较两个相同类型对象的字段修改
     *
     * @param oldObj 修改前实体
     * @param newObj 修改后实体
     * @param <T> 泛型类型
     * @return 修改的字段明细
     */
    public static <T> String diff(T oldObj, T newObj) {
        StringBuilder sb = new StringBuilder();
        try {
            Class<?> clazz = oldObj.getClass();
            Compare classLog = clazz.getAnnotation(Compare.class);
            if (classLog == null) {
                return "检查类上是否存在@LogCompare注解";
            }
            sb.append(classLog.name());

            Field[] fields = getAllFields(clazz);
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                Compare fieldLog = field.getAnnotation(Compare.class);
                if (fieldLog == null) {
                    continue;
                }

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                Method getMethod = pd.getReadMethod();
                Object oldValue = getMethod.invoke(oldObj);
                Object newValue = getMethod.invoke(newObj);
                // 都为null情况
                if (oldValue == newValue) {
                    continue;
                }
                if (null == oldValue) {
                    sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append("null").append(ARROWS).append(reflectValue(newValue, fieldLog, field));
                } else if (null == newValue) {
                    sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append(reflectValue(oldValue, fieldLog, field)).append(ARROWS).append("null");
                } else if (!oldValue.toString().equals(newValue.toString())) {
                    if (fieldLog.type() == Compare.Type.STRING) {
                        sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append(oldValue).append(ARROWS).append(newValue);
                    } else if (fieldLog.type() == Compare.Type.ENUM) {
                        Class<?> enumType = Class.forName(field.getGenericType().getTypeName());
                        Method getDesc = enumType.getMethod("getDesc");
                        sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append(getDesc.invoke(oldValue)).append(ARROWS)
                            .append(getDesc.invoke(newValue));
                    } else if (fieldLog.type() == Compare.Type.DATE) {
                        sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append(DateUtil.format((Date) oldValue, DateUtil.newFormat)).append(ARROWS)
                            .append(DateUtil.format((Date) newValue, DateUtil.newFormat));
                    }
                    if (fieldLog.type() == Compare.Type.JSON) {
                        sb.append(SymbolConstants.CHINESE_COMMA).append(fieldLog.name()).append(": ").append(JSON.toJSON(oldValue)).append(ARROWS).append(JSON.toJSON(newValue));
                    }
                }
            }
            String compare = sb.toString();
            return classLog.name().equals(compare) ? null : compare;
        } catch (Exception e) {
            logger.error("记录修改明细异常,oldObj:" + JSON.toJSON(oldObj) + ",newObj:" + JSON.toJSON(newObj), e);
        }
        return "记录修改明细日志异常";
    }

    /**
     * 获取字段值
     * @param value 对象
     * @param fieldLog fieldLog
     * @param field 字段对象
     * @return
     * @throws Exception
     */
    private static String reflectValue(Object value, Compare fieldLog, Field field) throws Exception {
        if (fieldLog.type() == Compare.Type.STRING) {
            return value.toString();
        } else if (fieldLog.type() == Compare.Type.ENUM) {
            Class<?> enumType = Class.forName(field.getGenericType().getTypeName());
            Method getDesc = enumType.getMethod("getDesc");
            return getDesc.invoke(value).toString();
        } else if (fieldLog.type() == Compare.Type.DATE) {
            return DateUtil.format((Date) value, DateUtil.newFormat);
        }
        throw new Exception("不支持的字段类型");
    }

    /**
     * 获取子类及其父类所有属性
     * @param clazz Class
     * @return Field[]
     */
    private static Field[] getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

}
